//
// Created by Administrator on 2024/5/11.
//

#include "EncoderMediaCodec.h"


int EncoderMediaCodec::init(int videoWidth, int videoHeight, int bitRate, jbyte fps, int gouSize) {
    this->width = videoWidth;
    this->height = videoHeight;
    avCodec = avcodec_find_encoder_by_name("h264_mediacodec");
//    avCodec = avcodec_find_encoder(AV_CODEC_ID_H264);
//    LOGNative(".....1，%d:", avCodec);
    if (avCodec == nullptr) {
        destroy();
        return -1;
    }
    avCodecContext = avcodec_alloc_context3(avCodec);
    if (!avCodecContext) {
        return -2;
    }
    avCodecContext->codec_id = AV_CODEC_ID_H264;
    avCodecContext->codec_type = AVMEDIA_TYPE_VIDEO;
    avCodecContext->pix_fmt = AV_PIX_FMT_YUV420P;
    avCodecContext->width = this->width;
    avCodecContext->height = this->height;
//    avCodecContext->b_frame_strategy = false;
    //bit_rate/-bt tolerance 设置视频码率容忍度kbit/s （固定误差）
    //	rc_max_rate/-maxrate bitrate设置最大视频码率容忍度 （可变误差）
    //	rc_min_rate/-minrate bitreate 设置最小视频码率容忍度（可变误差）
    //	rc_buffer_size/-bufsize size 设置码率控制缓冲区大小
    avCodecContext->bit_rate = 400 * 1024;
//    avCodecContext->rc_min_rate = static_cast<int64_t>(bitRate * 0.8);
//    avCodecContext->rc_max_rate = static_cast<int64_t>(bitRate * 1.2);
    //两个I帧之间的间隔
    avCodecContext->gop_size = 100;
//    avCodecContext->max_b_frames = 0;
//    avCodecContext->keyint_min = 25;
    //编码帧率，每秒多少帧。下面表示1秒25帧
//    avCodecContext->time_base.num = 1;// 华为M6 这样出来的参数是25帧 但是I帧间隔在27左右
//    avCodecContext->time_base.den = fps;
//    avCodecContext->qmax = 51;
//    avCodecContext->qmin = 10;
//    avCodecContext->thread_type = 1;
//    avCodecContext->thread_count = 2;
//    avCodecContext->thread_safe_callbacks = 1;
    AVDictionary *dictionary = 0;
//    AVDictionary *dictionary = nullptr;
//    if (avCodecContext->codec_id == AV_CODEC_ID_H264) {
//        //		preset的参数主要调节编码速度和质量的平衡，
//        //		有ultrafast、superfast、veryfast、faster、fast、medium、slow、slower、veryslow、placebo这10个选项，
//        //		从快到慢。
//        av_dict_set(&dictionary, "preset", "superfast", 0);
//        /*
//		tune的参数主要配合视频类型和视觉优化的参数。
//		tune的值有： film：  电影、真人类型；
//		animation：  动画；
//		grain：      需要保留大量的grain时用；
//		stillimage：  静态图像编码时使用；
//		psnr：      为提高psnr做了优化的参数；
//		ssim：      为提高ssim做了优化的参数；
//		fastdecode： 可以快速解码的参数；
//		zerolatency：零延迟，用在需要非常低的延迟的情况下，比如电视电话会议的编码。
//		*/
//        av_dict_set(&dictionary, "tune", "zerolatency", 0);
//        /*
//		画质,分别是baseline, extended, main, high
//		1、Baseline Profile：基本画质。支持I/P 帧，只支持无交错（Progressive）和CAVLC；
//		2、Extended profile：进阶画质。支持I/P/B/SP/SI 帧，只支持无交错（Progressive）和CAVLC；(用的少)
//		3、Main profile：主流画质。提供I/P/B 帧，支持无交错（Progressive）和交错（Interlaced）， 也支持CAVLC 和CABAC 的支持；
//		4、High profile：高级画质。在main Profile 的基础上增加了8x8内部预测、自定义量化、 无损视频编码和更多的YUV 格式；
//　　	H.264 Baseline profile、Extended profile和Main profile都是针对8位样本数据、4:2:0格式(YUV)的视频序列。在相同配置情况下，
//		High profile（HP）可以比Main profile（MP）降低10%的码率。 根据应用领域的不同，Baseline profile多应用于实时通信领域，
//		Main profile多应用于流媒体领域，High profile则多应用于广电和存储领域。
//		*/
//        av_dict_set(&dictionary, "profile", "high", 0);
////        av_dict_set(&dictionary, "profile", "main", 0);
//
//        av_dict_set(&dictionary, "framerate", "25", 0);
//    }
//    AVCodecParameters *parameters = avcodec_parameters_alloc();
//    avcodec_parameters_to_context(avCodecContext, parameters);
    int open2 = avcodec_open2(avCodecContext, avCodec, nullptr);
    if (open2 < 0) {
        return open2;
    }
    avFrame = av_frame_alloc();
    if (!avFrame) {
        destroy();
        return -3;
    }
    avFrame->format = avCodecContext->pix_fmt;
    avFrame->width = avCodecContext->width;
    avFrame->height = avCodecContext->height;
    avFrame->pts = 0;
    int i = av_frame_get_buffer(avFrame, 0);
//    int i = av_image_fill_linesizes(avFrame->linesize, avCodecContext->pix_fmt, avFrame->width);
    if (i < 0) {
        destroy();
        return -4;
    }
    int size = videoHeight * videoWidth;
    i420_v = static_cast<uint8_t *>(av_malloc(static_cast<size_t>(size / 4)));
    i420_u = static_cast<uint8_t *>(av_malloc(static_cast<size_t>(size / 4)));
    outData = static_cast<u_int8_t *>(av_malloc(static_cast<size_t>(videoWidth * videoHeight / 2)));
    avPacket = static_cast<AVPacket *>(av_malloc(sizeof(AVPacket)));
    av_init_packet(avPacket);
    return 0;
}

void EncoderMediaCodec::destroy() {

    if (avPacket != nullptr) {
        av_free(avPacket);
    }
    if (avFrame != nullptr) {
        av_free(avFrame);
    }
    if (avCodecContext != nullptr) {
        avcodec_close(avCodecContext);
        avcodec_free_context(&avCodecContext);
    }
    if (outData != nullptr) {
        av_free(outData);
    }
    if (i420_v != nullptr) {
        av_free(i420_v);
    }
    if (i420_u != nullptr) {
        av_free(i420_u);
    }
    delete yuvConvert;
}

int EncoderMediaCodec::encoderOneFrame(uint8_t *inBuff, uint8_t *outBuff) {
    //转换ARGB 到 420P
//    uint8_t *argbToI420 = yuvConvert->argbToI420(inBuff);
    av_packet_unref(avPacket);
    memcpy(avFrame->data, inBuff, avFrame->width * avFrame->height * 3 / 2);
    int frame = avcodec_send_frame(avCodecContext, avFrame);
    if (frame != 0) {
        return -1;
    }
    while (0 == avcodec_receive_packet(avCodecContext, avPacket)) {
        //回调解码完成的数据
        if (avPacket->size > 0) {
            memcpy(outBuff, avPacket->data, avPacket->size);
        }
    }
    return 0;
}

int EncoderMediaCodec::init2() {
    const char *codec_name;
    const AVCodec *codec;
    AVCodecContext *c = NULL;
    int i, ret, x, y;
    FILE *f;
    AVFrame *frame;
    AVPacket *pkt;
//    codec_name = "h264";

    /* find the mpeg1video encoder */
    codec = avcodec_find_encoder(AV_CODEC_ID_H264);
//    codec = avcodec_find_encoder_by_name(codec_name);
    if (!codec) {
        fprintf(stderr, "Codec '%s' not found\n", codec_name);
        return -1;
    }

    c = avcodec_alloc_context3(codec);
    if (!c) {
        fprintf(stderr, "Could not allocate video codec context\n");
        return -2;
    }

    pkt = av_packet_alloc();
    if (!pkt) {
        return -3;
    }
    /* put sample parameters */
    c->bit_rate = 400000;
    /* resolution must be a multiple of two */
    c->width = 352;
    c->height = 288;
    /* frames per second */
    c->time_base = (AVRational) {1, 25};
    c->framerate = (AVRational) {25, 1};

    /* emit one intra frame every ten frames
     * check frame pict_type before passing frame
     * to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
     * then gop_size is ignored and the output of encoder
     * will always be I frame irrespective to gop_size
     */
    c->gop_size = 10;
    c->max_b_frames = 1;
    c->pix_fmt = AV_PIX_FMT_YUV420P;

    if (codec->id == AV_CODEC_ID_H264)
        av_opt_set(c->priv_data, "preset", "slow", 0);

    /* open it */
    ret = avcodec_open2(c, codec, NULL);
    if (ret < 0) {
        fprintf(stderr, "Could not open codec: %s\n", av_err2str(ret));
        return ret;
    }
    return 0;
}
